export class ImageOperation {
    PIXEL_SIZE = 4;
    imageData = [];

    constructor(imageData) {
        this.imageData = imageData
    }

    /**
     * 色值平均
     */
    grayscale() {
        const data = this.imageData.data;
        for (let i = 0; i < data.length; i = i + this.PIXEL_SIZE) {
            const avg = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
            data[i] = avg;
            data[i + 1] = avg;
            data[i + 2] = avg;
            data[i + 3] = data[i + 3];
        }
        return this;
    }

    /**
     * 色值取反
     */
    invert() {
        const data = this.imageData.data;
        for (let i = 0; i < data.length; i = i + this.PIXEL_SIZE) {
            const max = 255;
            data[i] = max - data[i];
            data[i + 1] = max - data[i + 1];
            data[i + 2] = max - data[i + 2];
            data[i + 3] = data[i + 3];
        }
        return this;
    }

    /**
     * 压缩，输入为采样间隔
     * @param {Number} xStep 横向缺省个数
     * @param {Number} yStep 纵向缺省个数
     */
    compress(xStep = 1, yStep = 1) {
        const { data, width, height } = this.imageData;
        const buffer = [];
        let newHeight = 0;
        for (let i = 0; i < height; i = i + yStep) {
            newHeight++;
            for (let j = 0; j < width; j = j + xStep) {
                const cur = (i * width * this.PIXEL_SIZE + j * this.PIXEL_SIZE);
                buffer.push(...data.slice(cur, cur + this.PIXEL_SIZE));
            }
        }
        this.imageData = new ImageData(Uint8ClampedArray.from(buffer), buffer.length / this.PIXEL_SIZE / newHeight, newHeight);
        return this;
    }

    /**
     * 膨胀
     * @param {Number} xStep 横向复制个数
     * @param {Number} yStep 纵向复制个数
     */
    expand(xStep = 1, yStep = 1) {
        const { data, width, height } = this.imageData;
        const buffer = [];
        let newHeight = 0;
        for (let i = 0; i < height; i++) {
            for (let m = 0; m < yStep; m++) { // 纵向扩充
                newHeight++;
                for (let j = 0; j < width; j++) {
                    const cur = (i * width + j) * this.PIXEL_SIZE;
                    for (let n = 0; n < xStep; n++) { // 横向扩充
                        buffer.push(...data.slice(cur, cur + this.PIXEL_SIZE));
                    }
                }
            }
        }
        this.imageData = new ImageData(Uint8ClampedArray.from(buffer), buffer.length / this.PIXEL_SIZE / newHeight, newHeight);
        return this;
    }

    /**
     * 打点，输入为采样间隔
     * @param {Number} xStep 横向缺省个数
     * @param {Number} yStep 纵向缺省个数
     * @param {Function} fill 填充被挖去像素点的值
     */
    filter(xStep = 1, yStep = 1, fill = () => [255, 255, 255, 255]) {
        const { data, width, height } = this.imageData;
        const buffer = [];
        for (let i = 0; i < height; i = i + yStep) {
            for (let m = 0; m < yStep; m++) { // 纵向扩充
                if (m == 0) {
                    for (let j = 0; j < width; j = j + xStep) {
                        const cur = (i * width + j) * this.PIXEL_SIZE;
                        for (let n = 0; n < xStep; n++) { // 横向扩充
                            if (n == 0) {
                                buffer.push(...data.slice(cur, cur + this.PIXEL_SIZE));
                            } else {
                                buffer.push(...fill(i, j, n));
                            }
                        }
                    }
                } else {
                    for (let k = 0; k < width; k++) {
                        buffer.push(...fill(i, k, m));
                    }
                }
            }
        }
        this.imageData = new ImageData(Uint8ClampedArray.from(buffer), width);
        return this;
    }

    /**
     * 彩色
     */
    colorful(t) {
        const { data, width, height } = this.imageData;
        const buffer = [];
        for (let p = 0; p < data.length; p += this.PIXEL_SIZE) {
            const i = p / this.PIXEL_SIZE;
            const x = i % width;
            const y = (i / height) >>> 0;

            const r = 64 + (128 * x) / width + 64 * Math.sin(t / 1000);
            const g = 64 + (128 * y) / height + 64 * Math.cos(t / 1000);
            const b = 128;

            buffer[p + 0] = (r + data[p + 0]) / 2;
            buffer[p + 1] = (g + data[p + 1]) / 2;
            buffer[p + 2] = (b + data[p + 2]) / 2;
            buffer[p + 3] = (255 + data[p + 3]) / 2;
        }
        this.imageData = new ImageData(Uint8ClampedArray.from(buffer), width);
        return this;
    }
}